﻿namespace NTLib.IOC.UTest
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using NTLib.IOC.UTest.Resources;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Exceptions;

    [TestClass]
    public class IOContainerUTest
    {
        #region Fields

        public const string TEST_CATEGORY = "IOC";

        #endregion

        #region Methods

        /// <summary>
        /// Test the default creation of the IOContainer
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_Create()
        {
            using (IOContainer container = new IOContainer())
            {
                Assert.IsTrue(container.AllowInheritanceKeySearch);
                Assert.IsTrue(container.EnabledHierarchiSearch);
                Assert.AreNotEqual(default(Guid), container.Id);
            }
        }

        /// <summary>
        /// Test the storage of the same instance twice
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IOContainer_StoreSameInstanceTwice()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestA());
                container.Store<ITest>(new TestA());
            }
        }

        /// <summary>
        /// Test the storage of two instance with the same key
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void IOContainer_StoreTwoInstanceWithSameKey()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestA());
                container.Store<ITest>(new TestB());
            }
        }

        /// <summary>
        /// Test the storage of the same instance twice with the override set to true
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_StoreSameInstanceTwice_withoverride()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestA());
                container.Store<ITest>(new TestA(), true);
            }
        }

        /// <summary>
        /// Test the storage of two instance with the same key with the override set to true
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_StoreTwoInstanceWithSameKey_withoverride()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestA());
                container.Store<ITest>(new TestB(), true);
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by singleton process
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_GetSetInstance()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestA());

                var result = container.Get<ITest>();
                Assert.IsInstanceOfType(result, typeof(TestA));

                var result2 = container.Get<TestA>(typeof(ITest));
                Assert.IsInstanceOfType(result2, typeof(TestA));

                var result3 = container.Get<ITest>(typeof(ITest));
                Assert.IsInstanceOfType(result2, typeof(TestA));
            }
        }


        /// <summary>
        /// Test the storage and the retreive of the instance by singleton process with override
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_GetSetInstance_override()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>(new TestB());
                container.Store<ITest>(new TestA(), true);

                var result = container.Get<ITest>();
                Assert.IsInstanceOfType(result, typeof(TestA));

                var result2 = container.Get<TestA>(typeof(ITest));
                Assert.IsInstanceOfType(result2, typeof(TestA));

                var result3 = container.Get<ITest>(typeof(ITest));
                Assert.IsInstanceOfType(result2, typeof(TestA));
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by provider process with override
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_GetSetByProvider()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>((state, cancel) => { return new TestA(); });

                var result = container.Get<ITest>();
                Assert.IsInstanceOfType(result, typeof(TestA));
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by type process with override
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_GetSetByType()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<TestA, ITest>(false);

                var result = container.Get<ITest>();
                Assert.IsInstanceOfType(result, typeof(TestA));
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by type process with override
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_GetSetByTypeByPool()
        {
            string msg = "poney";
            using (IOContainer container = new IOContainer())
            {
                container.Store<TestA, ITest>(true);

                using (var result = container.Get<ITest>())
                {
                    Assert.IsInstanceOfType(result, typeof(TestA));
                    (result as TestA).Msg = msg;
                }

                var result2 = container.Get<ITest>();
                Assert.IsInstanceOfType(result2, typeof(TestA));
                Assert.AreEqual((result2 as TestA).Msg, msg);
            }
        }

        /// <summary>
        /// the container attach failed by null.
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void IOContainer_AttachFailed_ByNull()
        {
            using (var container = new IOContainer())
            {
                container.Attach(null);
            }
        }

        /// <summary>
        /// the container attach failed by attach itself.
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void IOContainer_AttachFailed_AttachItself()
        {
            using (var container = new IOContainer())
            {
                container.Attach(container);
            }
        }

        /// <summary>
        /// the container attach failed by attach a child.
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_AttachFailed_AttachAlreadyChild()
        {
            using (var container = new IOContainer())
            {
                using (var containerChild = new IOContainer())
                {
                    container.Attach(containerChild);
                    container.Attach(containerChild);
                }
            }
        }

        /// <summary>
        /// the container attach failed by attach container alreadyattach.
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(IOContainerAlreadyAttachedException))]
        public void IOContainer_AttachFailed_AttachAlreadyAttached()
        {
            using (var container = new IOContainer())
            {
                using (var containerChild = new IOContainer())
                {
                    container.Attach(containerChild);
                    using (var containerChild2 = new IOContainer())
                    {
                        containerChild.Attach(containerChild2);
                        container.Attach(containerChild2);
                    }
                }
            }
        }

        /// <summary>
        /// the container attach failed by attach container alreadyattach.
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        [ExpectedException(typeof(IOContainerAlreadyAttachedException))]
        public void IOContainer_AttachFailed_AttachParentToChild()
        {
            using (var container = new IOContainer())
            {
                using (var containerChild = new IOContainer())
                {
                    container.Attach(containerChild);
                    containerChild.Attach(container);
                }
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by through the relation
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_HerarchicalRetreive()
        {
            using (IOContainer container = new IOContainer())
            {
                Assert.IsNull(container.Parent);
                container.Store<ITest>((o, c) => new TestA());

                var testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));

                using (IOContainer containerTop = new IOContainer())
                {
                    var test = containerTop.Get<ITest>();
                    Assert.IsNull(test);

                    container.Attach(containerTop);

                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestA));

                    containerTop.Store<ITest>((o, c) => new TestB());
                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestB));
                }

                testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by through the relation with the predicate
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_HerarchicalRetreive_predicate()
        {
            using (IOContainer container = new IOContainer())
            {
                Assert.IsNull(container.Parent);
                container.Store<ITest>((o, c) => new TestA());

                var testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));

                using (IOContainer containerTop = new IOContainer())
                {
                    var test = containerTop.Get<ITest>();
                    Assert.IsNull(test);

                    container.Attach(containerTop);

                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestA));

                    containerTop.Store<ITest>((o, c) => new TestB());
                    test = containerTop.Get<ITest>(i => i is TestA);
                    Assert.IsInstanceOfType(test, typeof(TestA));
                }

                testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by through the relation
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_Forget()
        {
            using (IOContainer container = new IOContainer())
            {
                container.Store<ITest>((o, c) => new TestA());

                var test = container.Get<ITest>();
                Assert.IsInstanceOfType(test, typeof(TestA));

                container.Forget<ITest>();
                test = container.Get<ITest>();
                Assert.IsNull(test);
            }
        }

        /// <summary>
        /// Test the storage and the retreive of the instance by through the relation
        /// </summary>
        [TestCategory(TEST_CATEGORY)]
        [TestMethod]
        public void IOContainer_HerarchicalRetreive_DeepUnallowed()
        {
            using (IOContainer container = new IOContainer())
            {
                Assert.IsNull(container.Parent);
                container.Store<ITest>((o, c) => new TestA());

                var testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));

                using (IOContainer containerTop = new IOContainer())
                {
                    var test = containerTop.Get<ITest>();
                    Assert.IsNull(test);

                    container.Attach(containerTop);
                    containerTop.EnabledHierarchiSearch = false;

                    test = containerTop.Get<ITest>();
                    Assert.IsNull(test);

                    containerTop.EnabledHierarchiSearch = true;
                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestA));


                    containerTop.Store<ITest>((o, c) => new TestB());
                    containerTop.EnabledHierarchiSearch = false;
                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestB));

                    containerTop.EnabledHierarchiSearch = true;
                    test = containerTop.Get<ITest>();
                    Assert.IsInstanceOfType(test, typeof(TestB));
                }

                testP = container.Get<ITest>();
                Assert.IsInstanceOfType(testP, typeof(TestA));
            }
        }

        #endregion
    }
}
